
package org.sopt.dev.ImgKnight.agent.Controller;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.collections.set.SynchronizedSet;
import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

import org.sopt.dev.ImgKnight.agent.Agent;
import org.sopt.dev.ImgKnight.agent.NIO.NioObjectStreamHandler;
import org.sopt.dev.ImgKnight.agent.NIO.NioTelnetStreamHandler;

public class TelnetController {
	
	private Agent agnet = null;
	
	private Channel listenChannel = null;
	
	private Set<Channel> channelSet = null;
	
	private ExecutorService NioBossExecutor = null;
	
	private ExecutorService NioWorkerExecutor = null;
	
	private Logger serviceLogger = null;
	
	private Logger exceptionLogger = null;
	
	@SuppressWarnings("unchecked")
	public TelnetController() {
		agnet = Agent.getInstance();
		channelSet = SynchronizedSet.decorate(new HashSet<Channel>());
		serviceLogger = Logger.getLogger("agnet.service");
		exceptionLogger = Logger.getLogger("agnet.exception");
	}
	
	public void open() throws IllegalArgumentException {
		NioBossExecutor = Executors.newCachedThreadPool();
		NioWorkerExecutor = Executors.newCachedThreadPool();
		ChannelFactory factory 
			= new NioServerSocketChannelFactory(NioBossExecutor, NioWorkerExecutor);
		ServerBootstrap bootstrap = new ServerBootstrap(factory);	
		NioTelnetStreamHandler handler = new NioTelnetStreamHandler();
		bootstrap.getPipeline().addLast("handler", handler);
		bootstrap.setOption("child.tcpNoDelay", true);
		bootstrap.setOption("child.keepAlive", true);
		listenChannel = bootstrap.bind(new InetSocketAddress(agnet.getAgentConfig().ControllerPort));
		if( !listenChannel.isBound() )
			throw new IllegalArgumentException("ControllerPort port: "+agnet.getAgentConfig().ControllerPort+" Bound Exception");
	}
	
	public void close() {
		for(Channel c : channelSet)
			c.close();
		if( NioBossExecutor != null )
			NioBossExecutor.shutdownNow();
		if( NioWorkerExecutor != null )
			NioWorkerExecutor.shutdownNow();
		if( listenChannel != null )
			listenChannel.close().awaitUninterruptibly();
	}
	
	public void onConnectionEstablished(Channel channel) {
		serviceLogger.info("RemoteController accept: "+channel.getRemoteAddress().toString());
		channelSet.add(channel);
	}
	
	public void onConnectionClosed(Channel channel) {
		serviceLogger.info("RemoteController disconnect: "+channel.getRemoteAddress().toString());
		channel.close();
		channelSet.remove(channel);
	}
	
	public void onRecvedObject(Channel channel, Object obj) {
		String request = (String)obj;
		if( request.endsWith("\r\n") )
			request = request.substring(0, request.length()-2);
		int colon = request.indexOf(":");
		if( colon == -1 )
			return;
		String opcode = request.substring(0, colon);
		String oprand = request.substring(colon+1);
		if( opcode == null || oprand == null )
			return;
		if( opcode.equalsIgnoreCase("info") ) {
			if( oprand.equalsIgnoreCase("signiture") ) {
				channel.write("Accepted by ImgKnight-Agent On "+new Date()+"\r\n");
				channel.write("(C)Copyright 2009 Shout Our Passion Together(SOPT).\r\n\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("condition") ) {
				channel.write("info:condition="+agnet.getCondition()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("trafic") ) {
				Long transferred = NioObjectStreamHandler.getTransferredMessages();
				channel.write("info:trafic="+transferred.toString()+"\r\n");
				return;
			}
		}
		else if( opcode.equalsIgnoreCase("run") ) {
			if( oprand.equalsIgnoreCase("gc") ) {
				Runtime.getRuntime().gc();
				System.out.println("gc");
				return;
			}
		}
		exceptionLogger.error("unknown request from: "+channel.getRemoteAddress().toString()+", "+request);
		channel.write("Unsupported operation: "+request+"\r\n");
	}
	
}
